<template>
  <div>
    <el-dialog
      :title="isEdit? '编辑子部门' : '添加子部门'"
      :visible="dialogVisible"
      width="50%"
      :before-close="handleClose"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
    >
      <!-- 部门表单 -->
      <div>
        <el-form ref="deptForm" :model="form" :rules="rules" label-width="120px">
          <el-form-item label="部门名称" prop="name">
            <el-input v-model="form.name" style="width:80%" placeholder="1-10个字符" />
          </el-form-item>
          <el-form-item label="部门编码" prop="code">
            <el-input v-model="form.code" style="width:80%" placeholder="1-20个字符" />
          </el-form-item>
          <el-form-item label="部门负责人" prop="manager">
            <!-- 但是我们提前看了下新增部门的接口, 要的是负责人名字而不是id, 所以这里用value绑定名字
            问题: 假如, 页面显示名字, v-model绑定id, 怎么办?
            答案: el-option有2个属性, 一个叫label页面显示文字, 一个叫value是绑定的v-model的
            如果不写label, label的值默认与value一致
            -->
            <el-select v-model="form.manager" style="width:80%" placeholder="请选择">
              <el-option v-for="obj in employeeList" :key="obj.id" :value="obj.username" />
            </el-select>
          </el-form-item>
          <el-form-item label="部门介绍" prop="introduce">
            <el-input v-model="form.introduce" style="width:80%" placeholder="1-300个字符" type="textarea" :rows="3" />
          </el-form-item>
        </el-form>
      </div>

      <span slot="footer" class="dialog-footer">
        <el-button @click="cancelFn">取 消</el-button>
        <el-button type="primary" @click="enterFn">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
// .sync修饰符
// 使用: 在父级.vue文件内, 使用子组件传入props值 :props变量名.sync="父变量名"
// 作用: 在子组件内, this.$emit('update:props变量名', 值), 就可以直接给外面父变量名重新复制
// 好处: 父级不必再特意的绑定一个事件名和方法名, 然后在写赋值语句
// 问题: 本案例中, 上面:visible.sync="dialogVisible" 直接dialogVisible被el-dialog内部直接赋值了
// 直接导致props变量被修改->爆红
// 解决:
// 1. 把修改props的代码删掉(前提: 你确保外面不需要这个布尔值使用)
// 2. 如果你非要修改props变量(来源于外部)
// (1): 看看页面触发的条件, 比如点击让弹窗关闭, 监听点击事件, $emit回传外面事件方法, 让外面修改props的变量(单向数据流)
// (2): 定义data/computed属性, 初始值来自于props, 页面上data/computed, 子组件内, 再维护一个变量
// computed好一点: 内部组件页面修改computed值->set方法->$emit回传给父亲
// commputed取值: 内部页面要使用props的值, 可以computed->get方法中-> 给页面使用props的值
import { getEmployeeSimpleAPI, addDepartmentsAPI, getDepartDetailAPI, updateDepartmentsAPI } from '@/api'
export default {
  props: {
    // 控制弹窗显示
    dialogVisible: {
      type: Boolean,
      default: false
    },
    // 父级id
    parentId: {
      type: String,
      default: ''
    },
    // 编辑状态
    isEdit: {
      type: Boolean
    },
    // 用于非重验证（全部的部门数据）
    originList: {
      type: Array,
      // 数组/对象，prop默认值必须是函数形式, 返回默认数组/对象
      default: _ => [],
      // 相当于 default() {
      //   return []
      // }
      required: true
    }
  },
  data() {
    // 添加自定义校验函数 用于校验是否存在重复的部门code
    const validateCode = (rules, value, callback) => {
      // 提取数组里的code, 形成一个只有code的数组 方便下面使用includess
      let arr = this.originList.map(item => item.code)
      // 如果在编辑状态，要将编辑的这一项排除
      if (this.isEdit) {
        arr = this.originList.filter(item => item.id !== this.parentId).map(item => item.code)
      }
      // 如果存在该对象，则说明编码已经存在，抛出错误
      arr.includes(value) ? callback(new Error(`此部门编码'${value}, 已经存在`)) : callback()
    }
    // 非重验证部门名字
    const validateName = (rules, value, callback) => {
      // 这样写还是在跟所有人比, 不行的
      // const arr = this.originList.map(item => item.name)
      // 需求: 只能跟"同级"的部门名字比较
      // 解决: item的pid, 等于你点击的部门id(targetId)
      // 先找到添加子部门, 点击的这个部门的下属们
      // this.originList.filter(item => item.pid === this.targetId)
      let nameArr = this.originList.filter(item => item.pid === this.parentId).map(item => item.name)
      // 上面是新增, 编辑要走这里重新取值
      if (this.isEdit) {
        // 1. 点击的部门id, 对象
        const obj = this.originList.find(item => item.id === this.parentId)
        // 2. 取出它的pid
        const pid = obj.pid
        // 3. 整个列表里找到跟他同一个部门的那些人
        // 为何要加后面的, item也有它自己(编辑自己和自己冲突不行的), 把自己摘出去
        // 思路: item对象pid是和自己一样, 但是它的id如果也是目标id就证明是自己, 不能要
        nameArr = this.originList.filter(item => item.pid === pid && item.id !== this.parentId).map(item => item.name)
      }

      // 在判断最新输入的部门名称是否存在
      nameArr.includes(value) ? callback(new Error(`此部门名字'${value}',已经存在，换一个吧`)) : callback()
    }
    return {
      form: {
        name: '', // 部门名称
        code: '', // 部门编码
        manager: '', // 部门管理者
        introduce: '' // 部门介绍
      },
      // 表单规则校验
      rules: {
        name: [
          { required: true, message: '部门名称不能为空', trigger: 'blur' },
          { min: 1, max: 10, message: '部门名称要求1-10个字符', trigger: 'blur' },
          { validator: validateName, trigger: 'blur' }
        ],
        code: [
          { required: true, message: '部门编码不能为空', trigger: 'blur' },
          { min: 1, max: 20, message: '部门编码要求1-20个字符', trigger: 'blur' },
          { validator: validateCode, trigger: 'blur' }
        ],
        manager: [
          { required: true, message: '部门负责人不能为空', trigger: 'blur' }
        ],
        introduce: [
          { required: true, message: '部门介绍不能为空', trigger: 'blur' },
          { min: 1, max: 300, message: '部门介绍要求1-300个字符', trigger: 'blur' }
        ]
      },
      employeeList: [] // 员工列表

    }
  },
  // 解决2：侦听isEidt变量改变（状态改变）
  watch: {
    isEdit() {
      // 编辑状态为true 数据回显
      if (this.isEdit) {
        this.getDepartDetailFn()
      }
    }
  },
  async created() {
    // 调用API方法
    this.getEmployeesListFn()
  },
  methods: {
    // 弹窗关闭之前的事件 确认关闭
    handleClose(done) {
      // this.$confirm('确认关闭？')
      //   .then(_ => {
      //     done()
      //     // 用.sync修饰符语法 子组件传update:变量名
      //     this.$emit('update:dialogVisible', false)
      //   })
      //   .catch(_ => {})
      // 关闭前清空表单
      this.$refs.deptForm.resetFields()
      done()
    },
    // 点击取消关闭弹出层 重置表单
    cancelFn() {
      this.$refs.deptForm.resetFields()
      this.$emit('update:dialogVisible', false)
    },
    // 点击确认按钮 关闭弹出框， 调用更新接口 更新数据
    async enterFn() {
      // 做表单验证
      this.$refs.deptForm.validate(async valid => {
        if (valid) { // 如果通过验证
          // 判断编辑状态，如果true是调用更新接口，不是编辑状态调用添加部门接口
          if (this.isEdit) {
            const res = await updateDepartmentsAPI({
              ...this.form,
              id: this.parentId
            })
            this.$message.success(res.message)
          } else {
            // 添加部门
            const res = await addDepartmentsAPI({
              ...this.form,
              pid: this.parentId// 新增部门pid等于父级部门id
            })
            // console.log(res)
            this.$message.success(res.message)
          }

          this.$emit('update:dialogVisible', false)
          this.$emit('updateDataEv')// 让父级重新获取整个列表
        } else { // 没通过验证 不关闭弹窗
          this.$message.error('表单未通过验证哦')
        }
      })
      this.$refs.deptForm.resetFields()
    },
    // 请求部门负责人 - 列表
    // 这里为何不用数组接收?
    // 因为内部没有使用awaitTo中转
    // 错误响应会在响应拦截器统一处理
    // 除非, 像登录页面, 无论成功/失败, 都要关闭按钮的loading状态
    // try+catch或者awaitTo, 捕获await里的错误, 捕获后代码会继续向下执行
    // 如果你不捕获错误, 代码暂停往下执行
    // 获取员工的方法
    async getEmployeesListFn() {
      const res = await getEmployeeSimpleAPI()
      this.employeeList = res.data
      // console.log(res)
    },
    // 请求某个部门的详细信息 数据回显
    async getDepartDetailFn() {
      const res = await getDepartDetailAPI(this.parentId)
      // console.log(res)
      this.form = res.data// 数据回填->让页面显示这个默认数据 (可以)
      // 前端页面无影响: form对象现在有多对key+value, 但是页面上我们只用4个, 用什么取什么
      // 后端使用可能有影响: 如果后端对接口参数"个数"做判断, 就不行了, form对象一会儿点击确定要发给后台
    }
  }
}
</script>

<style>

</style>
